import { postJson, postBinary, okCode } from '../http'
import proto from '../../proto/proto'
import model from '../../model'

function convert(data, rowKeyDataType, valueDataTypeMap) {
  return data.map(
    row => ({
      rowKey: model.bytesToString(rowKeyDataType, row.rowKey),
      cells: (row.cells ?? []).map(
        cell => {
          const family = model.bytesToString('UTF-8', cell.family)
          const qualifier = model.bytesToString('UTF-8', cell.qualifier)
          return {
            family,
            qualifier,
            value: model.bytesToString(valueDataTypeMap[`${family}:${qualifier}`], cell.value)
          }
        }
      )
    })
  )
}

/**
 * 
 * @param {'frontend'|'backend'} mode 
 * @param {string} connectionId 
 * @param {string} tableName 
 * @param {string} rowKeyPattern 
 * @param {string} rowKeyDataType 
 * @param {Record<string, string>} valueDataTypeMap 
 * @returns 
 */
async function listRow(mode, connectionId, tableName, rowKeyPattern, rowKeyDataType, valueDataTypeMap) {
  if (mode === 'frontend') {
    try {
      const param = proto.encodeQueryParam(
        {
          connectionId: connectionId ?? '',
          tableName: tableName ?? '',
          rowKeyPattern: rowKeyPattern ?? new Uint8Array([])
        }
      )
      const response = await postBinary('/binary/row/query', new Blob([param]))
      let resp = { code: 0, message: '', data: [] }
      if (response.data.byteLength > 0) {
        resp = Object.assign(resp, proto.decodeRowObjectsResponse(new Uint8Array(response.data)))
      }
      if (resp.code == okCode) {
        return { ok: true, data: convert(resp.data, rowKeyDataType, valueDataTypeMap) }
      } else {
        return { ok: false, message: resp.message }
      }
    } catch (error) {
      return { ok: false, message: error.message }
    }
  } else {
    try {
      const resp = await postJson('/row/query', {
        connectionId,
        tableName,
        rowKeyPattern,
        rowKeyDataType,
        valueDataTypeMap
      })
      if (resp.ok) {
        return { ok: true, data: resp.data }
      } else {
        return { ok: false, message: resp.message }
      }
    } catch (error) {
      return { ok: false, message: error.message }
    }
  }
}

/**
 * 
 * @param {'frontend'|'backend'} mode 
 * @param {string} connectionId 
 * @param {string} tableName 
 * @param {string} rowKey 
 * @param {string} rowKeyDataType 
 * @param {{family: string, qualifier: string, value: string, valueDataType: string}[]} cells 
 * @returns 
 */
async function createRow(mode, connectionId, tableName, rowKey, rowKeyDataType, cells) {
  if (mode === 'frontend') {
    try {
      const param = proto.encodeCreateRowParam(
        {
          connectionId: connectionId ?? '',
          tableName: tableName ?? '',
          rowKey: model.stringToBytes(rowKeyDataType, rowKey),
          cells: (cells ?? []).map(
            cell => ({
              family: model.stringToBytes('UTF-8', cell.family),
              qualifier: model.stringToBytes('UTF-8', cell.qualifier),
              value: model.stringToBytes(cell.valueDataType, cell.value)
            })
          )
        }
      )
      const response = await postBinary('/binary/row/create', new Blob([param]))
      let resp = { code: 0, message: '', data: [] }
      if (response.data.byteLength > 0) {
        resp = Object.assign(resp, proto.decodeBooleanResponse(new Uint8Array(response.data)))
      }
      if (resp.code == okCode) {
        return { ok: true, data: resp.data }
      } else {
        return { ok: false, message: resp.message }
      }
    } catch (error) {
      return { ok: false, message: error.message }
    }
  } else {
    try {
      const resp = await postJson('/row/create', {
        connectionId,
        tableName,
        rowKey,
        rowKeyDataType,
        cells
      })
      if (resp.ok) {
        return { ok: true, data: resp.data }
      } else {
        return { ok: false, message: resp.message }
      }
    } catch (error) {
      return { ok: false, message: error.message }
    }
  }
}

/**
 * 
 * @param {'frontend'|'backend'} mode 
 * @param {string} connectionId 
 * @param {string} tableName 
 * @param {string[]} rowKeys 
 * @param {string} rowKeyDataType 
 * @returns 
 */
async function deleteRow(mode, connectionId, tableName, rowKeys, rowKeyDataType) {
  if (mode === 'frontend') {
    try {
      const param = proto.encodeDeleteRowParam(
        {
          connectionId: connectionId ?? '',
          tableName: tableName ?? '',
          rowKeys: (rowKeys ?? []).map(rowKey => model.stringToBytes(rowKeyDataType, rowKey))
        }
      )
      const response = await postBinary('/binary/row/delete', new Blob([param]))
      let resp = { code: 0, message: '', data: [] }
      if (response.data.byteLength > 0) {
        resp = Object.assign(resp, proto.decodeBooleanResponse(new Uint8Array(response.data)))
      }
      if (resp.code == okCode) {
        return { ok: true, data: resp.data }
      } else {
        return { ok: false, message: resp.message }
      }
    } catch (error) {
      return { ok: false, message: error.message }
    }
  } else {
    try {
      const resp = await postJson('/row/delete', {
        connectionId,
        tableName,
        rowKeys,
        rowKeyDataType
      })
      if (resp.ok) {
        return { ok: true, data: resp.data }
      } else {
        return { ok: false, message: resp.message }
      }
    } catch (error) {
      return { ok: false, message: error.message }
    }
  }
}

export default { listRow, createRow, deleteRow }
